#pragma once
#include "mysql_util.hpp"
#include <queue>
#include <mutex>
#include <vector>
#include <condition_variable>
#include <thread>

const int gnum = 5;
const int T = 30;

class MysqlPool
{
private:

    MysqlPool(const std::string& host,
    const std::string& user,
    const std::string& pass,
    const std::string& dbname,
    const uint16_t& port = 3306,
    const int maxnum = gnum):_host(host),_user(user),_pass(pass),_dbname(dbname),_gnum(maxnum),_checkTime(T)
    {
        initPool();
        _heartThread = std::thread(&MysqlPool::heartCheck,this);
    }
    void initPool()
    {
        for (int i = 0; i < _gnum;++i)
        {
            MYSQL *conn = mysql_util::mysql_create(_host, _user, _pass, _dbname, _port);
            if(conn != NULL)
            {
                _mysql_queue.push(conn);
            }
        }
    }
    void heartCheck()
    {
        while (true)
        {
            std::this_thread::sleep_for(std::chrono::seconds(_checkTime));
            std::unique_lock<std::mutex> lock(_mutex);
            if(_mysql_queue.empty())
            {
                continue;
            }
            std::queue<MYSQL *> tmp;
            while(!_mysql_queue.empty())
            {
                MYSQL *conn = _mysql_queue.front();
                _mysql_queue.pop();
                if(mysql_ping(conn) == 0)//开启MYSQL_OPT_RECONNECT 会自动重连但不是100%成功
                {
                    tmp.push(conn);
                }
                else
                {
                    mysql_close(conn);
                    MYSQL* newConn = mysql_util::mysql_create(_host, _user, _pass, _dbname, _port);
                    if(newConn!=nullptr)
                    {
                        tmp.push(newConn);
                    }
                }
            }
            _mysql_queue.swap(tmp);
        }
    }
public:
    static void initialize(const std::string& host,
    const std::string& user,
    const std::string& pass,
    const std::string& dbname,
    const uint16_t& port = 3306,
    const int maxnum = gnum)
    {
        std::unique_lock<std::mutex> lock(_singlock);
        if (mp == nullptr)
        {
            mp = new MysqlPool(host, user, pass, dbname, port, maxnum);
        }
    }
    static MysqlPool *getInstance()
    {
        if(mp != nullptr)
        {
            return mp;
        }
        return nullptr;
    }
    MYSQL* getConnection()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        while(_mysql_queue.empty())
        {
            _cond.wait(lock);
        }
        MYSQL* tmp = _mysql_queue.front();
        _mysql_queue.pop();
        return tmp;
    }
    void retConnection(MYSQL* conn)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if (conn == nullptr)
        {
            return;
        }
        _mysql_queue.push(conn);
        _cond.notify_one();
    }

private:
    std::string _host;
    std::string _user;
    std::string _pass;
    std::string _dbname;
    uint16_t   _port;
    int _gnum;
    std::mutex _mutex;
    std::queue<MYSQL *> _mysql_queue;
    std::condition_variable _cond;

    std::thread _heartThread;
    int _checkTime;

    static MysqlPool *mp;
    static std::mutex _singlock;
};
MysqlPool* MysqlPool::mp = nullptr;
std::mutex MysqlPool::_singlock;